package com.idlecode.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.smallbun.screw.core.Configuration;
import cn.smallbun.screw.core.engine.EngineConfig;
import cn.smallbun.screw.core.engine.EngineFileType;
import cn.smallbun.screw.core.engine.EngineTemplateType;
import cn.smallbun.screw.core.execute.DocumentationExecute;
import cn.smallbun.screw.core.process.ProcessConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.idlecode.bean.DataSourceEntity;
import com.idlecode.bean.TableEntity;
import com.idlecode.bean.TableFieldEntity;
import com.idlecode.config.DbType;
import com.idlecode.config.GenDataSource;
import com.idlecode.config.template.GeneratorConfig;
import com.idlecode.config.template.GeneratorInfo;
import com.idlecode.config.template.TemplateInfo;
import com.idlecode.util.ProjectUtil;
import com.idlecode.util.TemplateUtil;
import com.idlecode.util.VcsUtil;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.rmi.ServerException;
import java.sql.SQLException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: IdleCode
 * @description
 * @author: rongjuwei
 * @create: 2024-02-18 12:01
 **/
public class GeneratorService {

    public static void generateCode(Project project,
                                    DataSourceEntity dataSource,
                                    List<String> tableNames,
                                    String dataBaseName,
                                    List<String> selectedGenFileItems,
                                    String codeFormat) throws IOException, SQLException, InterruptedException {
        String projectBasePath = project.getBasePath();

        List<String> modelPathList = new ArrayList<>();
        List<String> filePathList = new ArrayList<>();
        for (String tableName : tableNames) {
            // 数据模型
            Map<String, Object> dataModel = getDataModel(project, dataSource, dataBaseName, tableName, selectedGenFileItems, codeFormat);
            if (dataModel == null) {
                continue;
            }
            String codeBasePath;
            Object codeBasePathObject = dataModel.get("codeBasePath");
            if (codeBasePathObject != null) {
                codeBasePath = (String) codeBasePathObject;
            } else {
                codeBasePath = "src/main/java";
            }

            String basePath = projectBasePath + ((codeBasePath.startsWith("/") || codeBasePath.startsWith("\\")) ? "" : "/") +
                    codeBasePath +
                    ((codeBasePath.endsWith("/") || codeBasePath.endsWith("\\")) ? "" : "/");

            modelPathList.add(codeBasePath + ((codeBasePath.endsWith("/") || codeBasePath.endsWith("\\")) ? "" : "/") +
                    dataModel.get("packageName") + "\\" + dataModel.get("packagePath"));
            // 代码生成器信息
            GeneratorConfig generatorConfig = new GeneratorConfig();
            GeneratorInfo generator = generatorConfig.getGeneratorConfig(selectedGenFileItems, codeFormat);
            // 渲染模板并输出
            for (TemplateInfo template : generator.getTemplates()) {
                dataModel.put("templateName", template.getTemplateName());
                String content = TemplateUtil.getContent(template.getTemplateContent(), dataModel);
                String shortFilePathStr = TemplateUtil.getContent(template.getGeneratorPath(), dataModel);
                filePathList.add(codeBasePath + ((codeBasePath.endsWith("/") || codeBasePath.endsWith("\\")) ? "" : "/") +
                        shortFilePathStr);
                //创建文件，将内容写入到指令文件中
                String filePathStr = basePath + shortFilePathStr;
                FileUtil.writeUtf8String(content, filePathStr);
            }
        }
        //将生成的代码自动提交到git或者svn
        String vcsType = VcsUtil.detectVcs(project);
        //String vcsType = "";
        if ("SVN".equals(vcsType)) {
            // 添加到 SVN 仓库
            int choice = Messages.showYesNoDialog(project, "代码生成成功！" +
                            "\n" +
                            "是否要将生成的文件添加到Subversion？", "提示",
                    Messages.getQuestionIcon());
            if (choice == Messages.YES) {
                for (String modelPath : modelPathList) {
                    String[] addCommand = {"svn", "add", modelPath, "--parents"};
                    executeCommand(addCommand, projectBasePath);
                }
            }
        } else if ("Git".equals(vcsType)) {
            // 添加到git仓库
            int choice = Messages.showYesNoDialog(project, "代码生成成功！" +
                            "\n" +
                            "是否要将生成的文件添加到Git？", "提示",
                    Messages.getQuestionIcon());
            // 添加到git仓库
            if (choice == Messages.YES) {
                for (String filePath : filePathList) {
                    String[] addCommand = {"git", "add", filePath};
                    executeCommand(addCommand, projectBasePath);
                }
            }
        }
    }

    /**
     * 获取渲染的数据模型
     *
     * @param tableName 表ID
     */
    private static Map<String, Object> getDataModel(Project project, DataSourceEntity dataSource, String dataBaseName, String tableName, List<String> selectedGenFileItems, String codeFormat) throws ServerException {
        GenDataSource genDataSource = null;
        try {
            genDataSource = new GenDataSource(dataSource, dataBaseName);
            if (genDataSource.getConnection() == null) {
                Messages.showMessageDialog("数据库连接失败", "异常", Messages.getErrorIcon());
                return null;
            }
            // 表信息
            TableEntity table = TableService.getTableEntry(genDataSource, dataBaseName, tableName, selectedGenFileItems, codeFormat);
            // 字段信息
            List<TableFieldEntity> fieldList = TableFieldService.getTableFieldEntity(genDataSource, dataBaseName, tableName);
            // 初始化字段数据
            TableFieldService.initFieldList(fieldList, selectedGenFileItems, codeFormat);
            table.setFieldList(fieldList);

            // 数据模型
            Map<String, Object> dataModel = new HashMap<>();

            // 获取数据库类型
            String dbType = dataSource.getDbType();
            dataModel.put("dbType", dbType);

            // 项目信息
            dataModel.put("package", table.getPackageName());
            dataModel.put("packageName", table.getPackageName());
            dataModel.put("packagePath", table.getModuleName().replace(".", File.separator));
            dataModel.put("codeBasePath", table.getCodeBasePath());
            dataModel.put("version", table.getVersion());
            dataModel.put("moduleName", table.getModuleName());
            dataModel.put("modulePath", table.getModuleName().replace(".", "/"));
            dataModel.put("modulePathTail", table.getClassName().toLowerCase());
            dataModel.put("ModuleName", StrUtil.upperFirst(table.getModuleName()));
            dataModel.put("functionName", table.getFunctionName());
            dataModel.put("FunctionName", StrUtil.upperFirst(table.getFunctionName()));

            // 开发者信息
            dataModel.put("author", table.getAuthor());
            dataModel.put("email", table.getEmail());
            dataModel.put("datetime", DateUtil.now());
            dataModel.put("date", DateUtil.today());

            // 设置字段分类
            setFieldTypeList(dataModel, table);

            // 设置基类信息
            setBaseClass(dataModel, table);

            // 导入的包列表
            List<String> importList = FieldTypeService.getPackage(fieldList);
            dataModel.put("importList", importList);

            // 表信息
            dataModel.put("tableName", table.getTableName());
            dataModel.put("tableComment", table.getTableComment());
            dataModel.put("className", StrUtil.lowerFirst(table.getClassName()));
            dataModel.put("ClassName", table.getClassName());
            dataModel.put("fieldList", table.getFieldList());

            // 生成路径
            dataModel.put("backendPath", table.getBackendPath());
            //dataModel.put("frontendPath", table.getFrontendPath());

            //java基础包
            dataModel.put("javaBasePackage", ProjectUtil.getJdkBasePackage(project));
            //tomcat基础包
            dataModel.put("tomcatBasePackage", ProjectUtil.getTomcatBasePackage(project));
            return dataModel;
        } catch (Exception e) {
            throw e;
        } finally {
            //关闭连接
            try {
                if (genDataSource.getConnection() != null) {
                    genDataSource.getConnection().close();
                }
            } catch (SQLException ex) {
            }
        }
    }

    /**
     * 设置基类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private static void setBaseClass(Map<String, Object> dataModel, TableEntity table) {
        if (table.getBaseclassId() == null) {
            return;
        }
/*
        // 基类
        BaseClassEntity baseClass = baseClassService.getById(table.getBaseclassId());
        baseClass.setPackageName(baseClass.getPackageName());
        dataModel.put("baseClass", baseClass);

        // 基类字段
        String[] fields = baseClass.getFields().split(",");

        // 标注为基类字段
        for (TableFieldEntity field : table.getFieldList()) {
            if (ArrayUtil.contains(fields, field.getFieldName())) {
                field.setBaseField(true);
            }
        }*/
    }

    /**
     * 设置字段分类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private static void setFieldTypeList(Map<String, Object> dataModel, TableEntity table) {
        // 主键列表 (支持多主键)
        List<TableFieldEntity> primaryList = new ArrayList<>();
        // 表单列表
        List<TableFieldEntity> formList = new ArrayList<>();
        // 网格列表
        List<TableFieldEntity> gridList = new ArrayList<>();
        // 查询列表
        List<TableFieldEntity> queryList = new ArrayList<>();

        for (TableFieldEntity field : table.getFieldList()) {
            if (field.isPrimaryPk()) {
                primaryList.add(field);
            }
            if (field.isGridItem()) {
                gridList.add(field);
            }
            if (field.isQueryItem()) {
                queryList.add(field);
            }
        }
        dataModel.put("primaryList", primaryList);
        dataModel.put("formList", formList);
        dataModel.put("gridList", gridList);
        dataModel.put("queryList", queryList);
    }

    /**
     * 将生成的代码添加git或svn
     *
     * @param command
     * @param workingDirectory
     * @throws IOException
     * @throws InterruptedException
     */
    private static void executeCommand(String[] command, String workingDirectory) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.directory(new File(workingDirectory));
        Process process = processBuilder.start();

        // 等待命令执行完毕
        int exitVal = process.waitFor();
        if (exitVal == 0) {
            System.out.println("Command executed successfully");
        } else {
            // 处理命令执行失败的情况
            System.err.println("Error executing command");
        }
    }

    public static void generateTableStructureDetail(
            Project project,
            DataSourceEntity dataSource,
            List<String> tableNames,
            String dataBaseName,
            String folderPath) throws IOException {
        if (CollUtil.isEmpty(tableNames)) {
            GenDataSource genDataSource = new GenDataSource(dataSource, dataBaseName);
            List<TableEntity> tableList = TableService.getTableList(genDataSource, dataBaseName);
            if (CollUtil.isNotEmpty(tableList)) {
                tableNames = tableList.stream().map(TableEntity::getTableName).collect(Collectors.toList());
            }
        }
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode objectNode = objectMapper.createObjectNode();
        for (String tableName : tableNames) {
            // 数据模型
            TableEntity table = getTableDetail(dataSource, dataBaseName, tableName);
            if (table == null) {
                continue;
            }
            ObjectNode tableObjectNode = objectMapper.createObjectNode();
            tableObjectNode.put("name", table.getTableName());
            tableObjectNode.put("comment", table.getTableComment());
            tableObjectNode.put("dbType", dataSource.getDbType());

            List<TableFieldEntity> fieldList = table.getFieldList();
            if (CollUtil.isNotEmpty(fieldList)) {
                ObjectNode fieldObjectNode = objectMapper.createObjectNode();
                for (TableFieldEntity tableFieldEntity : fieldList) {
                    ObjectNode fieldDetailObjectNode = objectMapper.createObjectNode();
                    String length;
                    Integer dataPrecision = tableFieldEntity.getDataPrecision();
                    if (dataPrecision != null && dataPrecision > 0) {
                        length = dataPrecision + "." + tableFieldEntity.getDataScale();
                    } else {
                        length = StrUtil.utf8Str(tableFieldEntity.getCharacterMaximumLength());
                    }
                    fieldDetailObjectNode.put("length", length);
                    fieldDetailObjectNode.put("type", tableFieldEntity.getFieldType());
                    fieldDetailObjectNode.put("comment", tableFieldEntity.getFieldComment());
                    fieldDetailObjectNode.put("name", tableFieldEntity.getFieldName());
                    fieldDetailObjectNode.put("required", tableFieldEntity.isNullable() ? "NO" : "YES");

                    fieldObjectNode.put(StrUtil.toCamelCase(tableFieldEntity.getFieldName().toLowerCase(Locale.ENGLISH)), fieldDetailObjectNode);
                }
                tableObjectNode.put("column", fieldObjectNode);
            }

            objectNode.put(StrUtil.toCamelCase(table.getTableName().toLowerCase(Locale.ENGLISH)), tableObjectNode);
        }

        // 创建并写入配置文件
        ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
        String jsonStr = objectWriter.writeValueAsString(objectNode);
        String now = DatePattern.PURE_DATETIME_FORMAT.format(new DateTime());
        String filePath = folderPath + File.separator + dataBaseName + "_" + tableNames.size() + "_" + now + ".json";
        File configFile = new File(filePath);
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8))) {
            writer.write(jsonStr);
        } catch (Exception e) {
            throw e;
        }

        // 自定义按钮文本
        String[] options = {"打开文件", "打开文件夹", "取消"};
        // 弹出提示框
        int result = Messages.showDialog(
                project,
                "文件生成成功！您想打开文件还是文件夹？",
                "选择操作",
                options,
                0, // 默认选中的按钮索引
                Messages.getQuestionIcon()
        );

        // 获取 LocalFileSystem 实例
        LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
        // 根据文件路径获取 VirtualFile 对象
        VirtualFile generatedFile = localFileSystem.findFileByPath(filePath);
        // 根据用户的选择执行相应的操作
        if (result == 0) {
            // 打开文件
            FileEditorManager.getInstance(project).openFile(generatedFile, true);
        } else if (result == 1) {
            // 打开文件夹
            VirtualFile parentDir = generatedFile.getParent();
            if (parentDir != null) {
                openFolderInSystemExplorer(parentDir);
            }
        }
    }

    /**
     * 获取渲染的数据模型
     *
     * @param tableName 表ID
     */
    private static TableEntity getTableDetail(DataSourceEntity dataSource, String dataBaseName, String tableName) throws ServerException {
        GenDataSource genDataSource = null;
        try {
            genDataSource = new GenDataSource(dataSource, dataBaseName);
            if (genDataSource.getConnection() == null) {
                Messages.showMessageDialog("数据库连接失败", "异常", Messages.getErrorIcon());
                return null;
            }
            // 表信息
            TableEntity table = TableService.getTable(genDataSource, dataBaseName, tableName);
            // 字段信息
            List<TableFieldEntity> fieldList = TableFieldService.getTableFieldEntity(genDataSource, dataBaseName, tableName);
            table.setFieldList(fieldList);
            return table;
        } catch (Exception e) {
            throw e;
        } finally {
            //关闭连接
            try {
                if (genDataSource.getConnection() != null) {
                    genDataSource.getConnection().close();
                }
            } catch (SQLException ex) {
            }
        }
    }

    private static void openFolderInSystemExplorer(VirtualFile folder) {
        if (folder == null) {
            return;
        }

        File file = new File(folder.getPath());
        if (file.exists() && Desktop.isDesktopSupported()) {
            Desktop desktop = Desktop.getDesktop();
            try {
                desktop.open(file.getAbsoluteFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void documentGeneration(
            Project project,
            DataSourceEntity dataSourceEntity,
            String dataBaseName,
            List<String> tableNames,
            String folderPath,
            EngineFileType engineFileType,
            String version,
            String description) {
        //文件名
        String fileName = handleFileOverwrite(project, engineFileType, dataBaseName, folderPath);
        //数据源
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName(DbType.getValue(dataSourceEntity.getDbType()).getDriverClass());
        hikariConfig.setJdbcUrl(dataSourceEntity.getConnUrl());
        hikariConfig.setUsername(dataSourceEntity.getUsername());
        hikariConfig.setPassword(dataSourceEntity.getPassword());
        //设置可以获取tables remarks信息
        hikariConfig.addDataSourceProperty("useInformationSchema", "true");
        hikariConfig.setMinimumIdle(2);
        hikariConfig.setMaximumPoolSize(5);
        DataSource dataSource = new HikariDataSource(hikariConfig);
        //生成配置
        EngineConfig engineConfig = EngineConfig.builder()
                //生成文件路径
                .fileOutputDir(folderPath)
                //打开目录
                .openOutputDir(false)
                //文件类型
                .fileType(engineFileType)
                //生成模板实现
                .produceType(EngineTemplateType.freemarker)
                //自定义文件名称
                .fileName(fileName).build();

        //忽略表
/*        ArrayList<String> ignoreTableName = new ArrayList<>();
        ignoreTableName.add("test_user");
        ignoreTableName.add("test_group");*/
        //忽略表前缀
  /*      ArrayList<String> ignorePrefix = new ArrayList<>();
        ignorePrefix.add("test_");*/
        //忽略表后缀
/*        ArrayList<String> ignoreSuffix = new ArrayList<>();
        ignoreSuffix.add("_test");*/
        ProcessConfig processConfig = ProcessConfig.builder()
                //指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时，将生成指定表，其余表不生成、并跳过忽略表配置
                //根据名称指定表生成
                .designatedTableName(tableNames)
                //根据表前缀生成
                .designatedTablePrefix(new ArrayList<>())
                //根据表后缀生成
                .designatedTableSuffix(new ArrayList<>())
                //忽略表名
                .ignoreTableName(new ArrayList<>())
                //忽略表前缀
                .ignoreTablePrefix(new ArrayList<>())
                //忽略表后缀
                .ignoreTableSuffix(new ArrayList<>()).build();
        //配置
        Configuration config = Configuration.builder()
                //版本
                .version(version)
                //描述
                .description(description)
                //数据源
                .dataSource(dataSource)
                //生成配置
                .engineConfig(engineConfig)
                //生成配置
                .produceConfig(processConfig)
                .build();
        //执行生成
        new DocumentationExecute(config).execute();

        // 自定义按钮文本
        String[] options = {"打开文件", "打开文件夹", "取消"};
        // 弹出提示框
        int result = Messages.showDialog(
                project,
                "文件生成成功！您想打开文件还是文件夹？",
                "选择操作",
                options,
                0, // 默认选中的按钮索引
                Messages.getQuestionIcon()
        );

        String filePath = folderPath + File.separator + fileName + engineFileType.getFileSuffix();
        // 获取 LocalFileSystem 实例
        LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
        // 根据文件路径获取 VirtualFile 对象
        VirtualFile generatedFile = localFileSystem.findFileByPath(filePath);
        // 根据用户的选择执行相应的操作
        if (result == 0) {
            // 打开文件
            FileEditorManager.getInstance(project).openFile(generatedFile, true);
        } else if (result == 1) {
            // 打开文件夹
            VirtualFile parentDir = generatedFile.getParent();
            if (parentDir != null) {
                openFolderInSystemExplorer(parentDir);
            }
        }
    }

    //文件覆盖
    public static String handleFileOverwrite(Project project, EngineFileType engineFileType, String fileName, String folderPath) {
        //文件路径
        String filePath = folderPath + File.separator + fileName + engineFileType.getFileSuffix();
        File file = new File(filePath);
        if (file.exists()) {
            int result = Messages.showYesNoDialog(
                    project,
                    fileName + engineFileType.getFileSuffix() + " 已经存在。" + "\r\n" + "是否覆盖？",
                    "确认另存为",
                    Messages.getQuestionIcon()
            );

            if (result == Messages.YES) {
                return fileName;
            } else if (result == Messages.NO) {
                // 用户选择不覆盖，生成新的文件名
                File newFile = getUniqueFileName(file);
                String newFileName = newFile.getName();
                return newFileName.substring(0, newFileName.lastIndexOf('.'));
            }
        }
        return fileName;
    }

    //获取唯一的文件名
    private static File getUniqueFileName(File file) {
        File newFile = file;
        int i = 1;
        while (newFile.exists()) {
            String filePath = file.getAbsolutePath();
            int dotIndex = filePath.lastIndexOf('.');
            // 如果文件有扩展名
            String baseName = filePath.substring(0, dotIndex);
            String extension = filePath.substring(dotIndex);
            newFile = new File(baseName + "(" + i + ")" + extension);
            i++;
        }
        return newFile;
    }
}
